package com.lmind.core.async;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;

public class FutureResult<V> implements Future<V> {

	private AtomicReference<Status> status;

	private Object locker = new Object();

	public FutureResult() {
		Status status = new Status();
		status.isDone = false;
		status.value = null;
		status.interrupted = false;
		this.status = new AtomicReference<Status>(status);
	}

	public void done(V value) {

		Status status = new Status();
		status.value = value;
		status.isDone = true;
		this.status.set(status);

		synchronized (locker) {
			locker.notifyAll();
		}
	}

	public void interrupt() {

		Status status = new Status();
		status.interrupted = true;
		this.status.set(status);

		synchronized (locker) {
			locker.notifyAll();
		}
	}

	@Override
	public boolean cancel(boolean mayInterruptIfRunning) {
		return false;
	}

	@Override
	public boolean isCancelled() {
		return false;
	}

	@Override
	public boolean isDone() {
		return status.get().isDone;
	}

	@Override
	public V get() throws InterruptedException, ExecutionException {
		try {
			return get(0, TimeUnit.MILLISECONDS);
		} catch (TimeoutException e) {
			return null;
		}
	}

	@Override
	public V get(long timeout, TimeUnit unit) throws InterruptedException,
			ExecutionException, TimeoutException {

		waitFor(unit.toMillis(timeout));

		return status.get().value;
	}

	private void waitFor(long timeout) throws TimeoutException,
			InterruptedException {
		
		if (isDone())
			return;
		
		synchronized (locker) {
			if (isDone())
				return;

			long now = System.currentTimeMillis();

			locker.wait(timeout);
			if (status.get().interrupted) {
				throw new InterruptedException();
			} else if (timeout != 0
					&& timeout + now > System.currentTimeMillis()) {
				throw new TimeoutException();
			}
		}
	}

	class Status {
		public V value;
		public boolean isDone;
		public boolean interrupted;
	}

}
